//  这句话用来编程时自动提示，运行时需要注释掉！
//import * as BABYLON from '@babylonjs/core';

var canvas = document.getElementById("renderCanvas");

var startRenderLoop = function (engine, canvas) {
  engine.runRenderLoop(function () {
    if (sceneToRender && sceneToRender.activeCamera) {
      sceneToRender.render();
    }
  });
};

var engine = null;
var scene = null;
var sceneToRender = null;
var createDefaultEngine = function () {
  return new BABYLON.Engine(canvas, true, {
    preserveDrawingBuffer: true,
    stencil: true,
    disableWebGL2Support: false,
  });
};


//  创建场景（相关代码都在此处）
var createScene = function () {
  // This creates a basic Babylon Scene object (non-mesh)
  var scene = new BABYLON.Scene(engine);

  //  创建相机(跟随dude)
  const camera = new BABYLON.FollowCamera("camera", new BABYLON.Vector3(0, 6, 0), scene);
  camera.radius = 1;
  camera.heightOffset = 1;
  camera.rotationOffset = 0;
  camera.cameraAcceleration = 0.0;
  camera.maxCameraSpeed = 0;
  
  camera.attachControl(canvas, true);

  camera.upperBetaLimit = Math.PI / 2.2;

  //  创建灯光
  var light = new BABYLON.DirectionalLight("light", new BABYLON.Vector3(0, -1, 1));
  light.position = new BABYLON.Vector3(0, 50, -100);

  //  显示坐标轴
  showAxis(10, scene);

  //  导入村庄,ground接收阴影
  BABYLON.SceneLoader.ImportMeshAsync("", "https://assets.babylonjs.com/meshes/", "valleyvillage.glb").then((result) => {
    scene.getMeshByName("ground").receiveShadows = true;
  });

  //  创建skybox
  const skybox = BABYLON.MeshBuilder.CreateBox("skybox", { size: 150 }, scene);
  const skyboxMaterial = new BABYLON.StandardMaterial("skyboxMaterial", scene);
  skyboxMaterial.backFaceCulling = false; //  关闭背面剔除
  //  立方盒子纹理(使用cubeTexture)（使用在线地址）
  //skyboxMaterial.reflectionTexture = new BABYLON.CubeTexture("Skybox/SkyCube2k/skyCube2k", scene);
  skyboxMaterial.reflectionTexture = new BABYLON.CubeTexture("https://playground.babylonjs.com/textures/skybox", scene);
  skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
  skyboxMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0);
  skyboxMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
  skybox.material = skyboxMaterial;

  //  创建一个行走的类(转弯角度和行走距离)
  const walk = function(turn, dist){
    this.turn = turn;
    this.dist = dist;
  }

  const track = [];
  track.push(new walk(86, 7));
  track.push(new walk(-85, 14.8));
  track.push(new walk(-93, 16.5));
  track.push(new walk(48, 25.5));
  track.push(new walk(-112, 30.5));
  track.push(new walk(-72, 33.2));
  track.push(new walk(42, 37.5));
  track.push(new walk(-98, 45.2));
  track.push(new walk(0, 47));

  //  阴影产生器
  const shadowGenerator = new BABYLON.ShadowGenerator(1024, light);

  //  导入dude
  BABYLON.SceneLoader.ImportMeshAsync("him", "https://playground.babylonjs.com/scenes/Dude/", "Dude.babylon", scene).then((result) => {
    const dude = result.meshes[0];
    dude.scaling = new BABYLON.Vector3(0.008, 0.008, 0.008);

    //  将相机绑定到dude上,这样
    camera.lockedTarget = dude;

    //  添加阴影
    shadowGenerator.addShadowCaster(dude, true);

    //  初始化dude的位置和旋转
    dude.position = new BABYLON.Vector3(-6, 0, 0);
    dude.rotate(BABYLON.Axis.Y, BABYLON.Tools.ToRadians(-95), BABYLON.Space.LOCAL);
    const startRotation = dude.rotationQuaternion.clone();

    //  播放骨骼动画
    scene.beginAnimation(result.skeletons[0], 0, 100, true, 1.0);

    let distance = 0.0;
    const step = 0.01;
    let idx = 0;

    //  场景每次渲染前，更新dude的位置
    scene.onBeforeRenderObservable.add(() => {
      //  向前移动
      dude.movePOV(0, 0, step);
      distance += step;

      if (distance > track[idx].dist) {
        dude.rotate(BABYLON.Axis.Y, BABYLON.Tools.ToRadians(track[idx].turn), BABYLON.Space.LOCAL);
        idx++;
        idx = idx % track.length;
        if (idx === 0) {
          distance = 0;
          dude.position = new BABYLON.Vector3(-6, 0, 0);
          dude.rotationQuaternion = startRotation.clone();
        }
      }
    });

  });


  return scene;
};




//  创建文字平面
const makeTextPlane = (text, color, size) => {
  const dynamicTexture = new BABYLON.DynamicTexture("dynamicTexture", 50, scene, true);
  dynamicTexture.hasAlpha = true;
  dynamicTexture.drawText(text, 5, 40, "bold 36px Arial", color, "transparent", true);

  const plane = BABYLON.MeshBuilder.CreatePlane("plane", { size: size, updatable: true }, scene);
  plane.material = new BABYLON.StandardMaterial("planeMat", scene);
  plane.material.backFaceCulling = false;
  plane.material.specularColor = new BABYLON.Color3(0, 0, 0);
  plane.material.diffuseTexture = dynamicTexture;
  return plane;
}

const showAxis = (length, scene) => {

  //  创建坐标轴x
  const xAxis = BABYLON.MeshBuilder.CreateLines("xAxis",
    {
      points: [new BABYLON.Vector3(0, 0, 0), new BABYLON.Vector3(length, 0, 0),
      new BABYLON.Vector3(length * 0.95, 0.1, 0), new BABYLON.Vector3(length, 0, 0),
      new BABYLON.Vector3(length * 0.95, -0.1, 0)]
    }, scene);
  xAxis.color = new BABYLON.Color3(1, 0, 0);
  const xText = makeTextPlane("X", "red", length / 10.0);
  xText.position = new BABYLON.Vector3(length * 0.95, -0.05 * length, 0);

  //  创建坐标轴y
  const yAxis = BABYLON.MeshBuilder.CreateLines("yAxis",
    {
      points: [new BABYLON.Vector3(0, 0, 0), new BABYLON.Vector3(0, length, 0),
      new BABYLON.Vector3(-0.1, length * 0.95, 0), new BABYLON.Vector3(0, length, 0),
      new BABYLON.Vector3(0.1, length * 0.95, 0)]
    }, scene);
  yAxis.color = new BABYLON.Color3(0, 1, 0);
  const yText = makeTextPlane("Y", "green", length / 10.0);
  yText.position = new BABYLON.Vector3(0.05 * length, length * 0.95, 0);

  //  创建坐标轴z
  const zAxis = BABYLON.MeshBuilder.CreateLines("zAxis",
    {
      points: [new BABYLON.Vector3(0, 0, 0), new BABYLON.Vector3(0, 0, length),
      new BABYLON.Vector3(0, -0.1, length * 0.95), new BABYLON.Vector3(0, 0, length),
      new BABYLON.Vector3(0, 0.1, length * 0.95)]
    }, scene);
  zAxis.color = new BABYLON.Color3(0, 0, 1);
  const zText = makeTextPlane("Z", "blue", length / 10.0);
  zText.position = new BABYLON.Vector3(0.05 * length, 0.05 * length, length * 0.95);
}


//================================================

window.initFunction = async function () {
  var asyncEngineCreation = async function () {
    try {
      return createDefaultEngine();
    } catch (e) {
      console.log(
        "the available createEngine function failed. Creating the default engine instead"
      );
      return createDefaultEngine();
    }
  };

  window.engine = await asyncEngineCreation();
  if (!engine) throw "engine should not be null.";
  startRenderLoop(engine, canvas);
  window.scene = createScene();
};
initFunction().then(() => {
  sceneToRender = scene;
});

// Resize
window.addEventListener("resize", function () {
  engine.resize();
});